import { createApp, h, type Ref } from "vue";
import type { DanmakuItem, DanmakuProps, DanmakuSlots } from "./_interface";

export const useDraw = (
  wrapper: Ref<HTMLDivElement | undefined>,
  props: DanmakuProps,
  slots: DanmakuSlots
) => {
  const whiteColors = ["#fff", "#ffffff", "#ffffffff", "white", undefined];
  /** 弹幕轨道，每个轨道只存放该轨道上最后一个弹幕 */
  const tunnels: Record<DanmakuItem["type"], (DanmakuItem | null)[]> = {
    top: [],
    bottom: [],
    move: [],
  };

  /** 获取弹幕宽度 */
  const measureElWidth = (el: HTMLElement) => {
    const visible = el.style.visibility;
    el.style.visibility = "hidden";
    // 添加到文档流中计算宽度
    wrapper.value?.appendChild(el);

    const width = el.offsetWidth;

    // 复原
    wrapper.value?.removeChild(el);
    el.style.visibility = visible;

    return width;
  };

  const draw = (danmas: DanmakuItem[]) => {
    if (!danmas.length || !wrapper.value) return;

    const wrapperW = wrapper.value.offsetWidth;
    const wrapperH = wrapper.value.offsetHeight;
    /** 轨道数量 */
    const tunnelCount = wrapperH / props.lineHeight! - 1;
    /** 选择轨道 */
    const chooseTunnel = (danma: DanmakuItem) => {
      // 最优轨道，在没有合适的轨道选择时有用
      let optimalTunnel = -1;
      let optimalTimeSub: number | null = null;
      for (let i = 0; i < tunnelCount; i++) {
        const danItem = tunnels[danma.type][i];
        // 轨道上不存在弹幕直接作为新弹幕的轨道
        if (!danItem) return [i];
        // 固定弹幕不能在已有弹幕的轨道上显示
        if (danma.type !== "move") continue;

        // 该轨道上已存在弹幕，计算若将此弹幕放于此是否会重叠
        const danMr = computeDanmaMR(danItem.$el!);
        // 距离小于配置间距, 不可被选中
        if (danMr <= props.horizontalSpacing!) continue;
        // 若速度大于此弹幕的速度，则不会重叠
        if (danItem.$speed! >= danma.$speed!) return [i];
        // 此弹幕追不上
        const timeSub =
          (wrapperW - props.horizontalSpacing!) / danma.$speed! -
          (wrapperW - danMr) / danItem.$speed!;
        if (timeSub >= 0) return [i];
        // 记录当前最优轨道
        if (optimalTimeSub === null) {
          optimalTunnel = i;
          optimalTimeSub = timeSub;
        } else {
          if (timeSub > optimalTimeSub) {
            optimalTunnel = i;
            optimalTimeSub = timeSub;
          }
        }
      }
      if (optimalTunnel === -1) {
        // 还是没有找到合适的轨道，则返回一个随机轨道并记录
        optimalTunnel = Math.floor(Math.random() * tunnelCount);
      }
      return [-1, optimalTunnel];
    };

    const docFragment = document.createDocumentFragment();
    for (const danma of danmas) {
      // 类型屏蔽
      if (props.visible?.[danma.type] === false) continue;
      // 彩色屏蔽
      if (props.visible?.color === false && !whiteColors.includes(danma.color))
        continue;

      // 初始化dom对象
      danma.$el = initDanmaEl(danma);
      // 计算宽度
      danma.$width = measureElWidth(danma.$el);
      // 计算速度
      danma.$speed = (wrapperW + danma.$width) / (danma.duration || 5);
      // 获取轨道
      const [tunnelIdx, optimalTunnelIdx] = chooseTunnel(danma);
      danma.$tunnelIdx = tunnelIdx;

      if (danma.$tunnelIdx === -1) {
        // 允许重叠，则将最佳轨道赋予弹幕
        if (props.allowOverlap) danma.$tunnelIdx = optimalTunnelIdx;
        else continue;
      }
      tunnels[danma.type][danma.$tunnelIdx] = danma;

      if (danma.type === "top") {
        danma.$el.style.top = `${props.lineHeight! * danma.$tunnelIdx}px`;
      } else if (danma.type === "bottom") {
        danma.$el.style.bottom = `${props.lineHeight! * danma.$tunnelIdx}px`;
      } else if (danma.type === "move") {
        danma.$el.style.top = `${props.lineHeight! * danma.$tunnelIdx}px`;
        danma.$el.style.transform = `translateX(-${wrapperW}px)`;
      }
      docFragment.appendChild(danma.$el);
    }
    wrapper.value.appendChild(docFragment);
  };

  /** 计算弹幕距离容器右边的距离 */
  const computeDanmaMR = (danmaEl: HTMLElement) => {
    // 容器右边距离
    const wr = wrapper.value!.getBoundingClientRect().right;

    // 弹幕元素右边距离
    let dr = danmaEl.getBoundingClientRect().right;
    if (!dr) dr = wr + (danmaEl.offsetWidth || +danmaEl.style.width);
    return wr - dr;
  };

  const initDanmaEl = (danma: DanmakuItem) => {
    let el: HTMLElement;
    if (slots.default) {
      // 使用插槽渲染弹幕
      const compoent = createApp({
        render() {
          return h("div", {}, slots.default!(danma));
        },
      });
      el = compoent.mount(document.createElement("div")).$el;
    } else {
      // 使用默认渲染
      el = document.createElement("div");
      el.innerText = danma.text;
    }

    // 设置样式
    if (danma.style) el.setAttribute("style", danma.style);
    if (danma.class) el.setAttribute("class", danma.class);
    el.style.color = danma.color || "white";
    el.style.fontSize = `${danma.size || 16}px`;
    el.classList.add("n-danma-item");

    // 标注彩色弹幕
    if (!whiteColors.includes(danma.color))
      el.classList.add("n-danma-item__color");
    // 标注弹幕类型
    el.classList.add(`n-danma-item__${danma.type}`);

    el.style.animationDuration = `${danma.duration || 5}s`;

    // 动画结束移除
    el.addEventListener("animationend", () => {
      wrapper.value?.removeChild(el);
      const elIdx = tunnels[danma.type].findIndex((item) => item?.$el === el);
      if (elIdx !== -1) tunnels[danma.type][elIdx] = null;
    });

    return el;
  };

  return { draw };
};
